package com.art.coconut.utils;

import com.art.coconut.annotation.CollectionFlag;
import com.art.coconut.annotation.DiffIgnore;
import com.art.coconut.annotation.EntityTag;
import com.art.coconut.annotation.PropertyComparator;
import com.art.coconut.annotation.PropertyField;
import com.art.coconut.annotation.UnionId;
import com.art.coconut.change.TypePojo;
import com.art.coconut.compare.BaseComparator;
import com.art.coconut.enums.ExceptionEnum;
import com.art.coconut.enums.PropertyTypeEnum;
import com.art.coconut.exception.CoconutCommonException;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

public class ProcessHandle {
    public static String getUniqueId(Object obj) throws IllegalAccessException {
        String key = null;
        Field[]fields = obj.getClass().getDeclaredFields();
        StringBuilder idSb = new StringBuilder();
        boolean hasUniqueId = Boolean.FALSE;
        for (Field field : fields) {
            UnionId id = field.getAnnotation(UnionId.class);
            if(id!=null){
                hasUniqueId = true;
                field.setAccessible(true);
                idSb.append(field.get(obj));
                idSb.append("@");
            }
        }
        if(hasUniqueId){
            key = idSb.substring(0, idSb.lastIndexOf("@"));
        }
        if(key==null){
            throw new CoconutCommonException(ExceptionEnum.NO_ID_EXCEPTION.getCode(), ExceptionEnum.NO_ID_EXCEPTION.getMsg());
        }
        return key;
    }
    public static String getEntityTagValue(Class<?> tClass){
        EntityTag tag = tClass.getAnnotation(EntityTag.class);
        return Objects.isNull(tag) ? null: tag.value();
    }
    public static String getEntityTagName(Class<?> tClass){
        EntityTag tag = tClass.getAnnotation(EntityTag.class);
        return Objects.isNull(tag) ? null: tag.name();
    }
    public static Map<Object, Object> collectionToMap(Collection<?> version) throws IllegalAccessException {
        Map<Object, Object> versionMap = new HashMap<>();
        for (Object o : version) {
            String key = getUniqueId(o);
            versionMap.put(key, o);
        }
        return versionMap;
    }
    public static Map<String, TypePojo> objectToMap(Object object,  Class<?> refClass) throws IllegalAccessException, InstantiationException {
        if(Objects.isNull(object)){
            return new LinkedHashMap<>();
        }
        Field[]fields = refClass.getDeclaredFields();
        List<TypePojo> typePojoList = new ArrayList<>(fields.length);
        String entityValue = null;
        String entityName = null;
        EntityTag entityTag = refClass.getAnnotation(EntityTag.class);
        if(entityTag != null){
            entityValue = entityTag.value();
            entityName = entityTag.name();
        }else{
            throw new CoconutCommonException(ExceptionEnum.NO_ENTITY_TAG_EXCEPTION.getCode(), ExceptionEnum.NO_ENTITY_TAG_EXCEPTION.getMsg());
        }
        for (Field field : fields) {
            DiffIgnore diffIgnore = field.getAnnotation(DiffIgnore.class);
            if(diffIgnore==null){
                TypePojo typePojo = new TypePojo();
                field.setAccessible(true);
                typePojo.setTypeName(field.getType().getTypeName());
                PropertyField propertyNameAno = field.getAnnotation(PropertyField.class);
                String propertyName = field.getName();
                if(propertyNameAno!=null){
                    propertyName = propertyNameAno.value();
                    typePojo.setOrder(propertyNameAno.order());
                }
                CollectionFlag collectionFlag = field.getAnnotation(CollectionFlag.class);
                if(collectionFlag == null){
                    PropertyComparator comparator = field.getAnnotation(PropertyComparator.class);
                    if(comparator!=null){
                        typePojo.setComparator(comparator.value().newInstance());
                    }else{
                        typePojo.setComparator(new BaseComparator());
                    }
                    typePojo.setTypeName(PropertyTypeEnum.BASE_TYPE.getTypeName());
                }else {
                    typePojo.setTypeName(collectionFlag.value().getTypeName());
                }
                typePojo.setTypeClass(field.getDeclaringClass());
                typePojo.setPropertyName(propertyName);
                typePojo.setType(field.getType());
                typePojo.setObject(field.get(object));
                typePojo.setEntityTag(entityValue);
                typePojo.setEntityNm(entityName);
                typePojo.setProperty(field.getName());
                typePojoList.add(typePojo);
//                resMap.put(propertyName, typePojo);
            }
        }
//        Map<String, TypePojo> linkedMap = new LinkedHashMap<>();
//        resMap.values().stream().sorted(Comparator.comparing(TypePojo::getOrder)).forEach(t->{
//            linkedMap.put(t.getPropertyName(), t);
//        });
        return typePojoList.stream()
                .sorted(Comparator.comparing(TypePojo::getOrder))
                .collect(Collectors.toMap(TypePojo::getProperty, e->e, throwingMerger(), LinkedHashMap::new));
    }
    private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
    }
}
